/*
   Copyright (C) 2009 Geoffrey Hausheer

This file is part of gpsim.

gpsim is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.

gpsim is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with gpsim; see the file COPYING.  If not, write to
the Free Software Foundation, 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.  */

/*

  push_button.cc


*/

/* IN_MODULE should be defined for modules */
#define IN_MODULE

#include <time.h>
#include <stdio.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#include <errno.h>
#include <stdlib.h>
#include <string>
#include <iostream>

//#include "../config.h"    // get the definition for HAVE_GUI
#ifdef HAVE_GUI

#include <gtk/gtk.h>

#include <gpsim/packages.h>
#include "potentiometer.h"

//----------------------------------------
class RTotalAttribute : public Float {

public:
  Potentiometer *pot;


  RTotalAttribute(Potentiometer *ppot)
    : Float("resistance",20000.0,"total resistance of potentiometer"),
      pot(ppot)
  {
  }


  virtual void set(double r) {

    Float::set(r);

    if(pot)
      pot->compute_VthZth();
  };

  void set(int r) {
    Float::set((double)r);
  };
};

class RatioAttribute : public Float {

public:
  Potentiometer *pot;


  RatioAttribute(Potentiometer *ppot)
    : Float("ratio",0.5,"ratio of resistances"),
      pot(ppot)
  {
  }

  virtual void set(double r) {
    if(r < 0) {
      r = 0;
    } else if(r > 1.0) {
      r = 1.0;
    }
    Float::set(r);

    if(pot) {
      pot->compute_VthZth();
      pot->updateSlider(r);
    }
  };

  void set_only(double r) {
    if(r < 0) {
      r = 0;
    } else if(r > 1.0) {
      r = 1.0;
    }
    Float::set(r);

    if(pot)
      pot->compute_VthZth();
  };

  void set(int r) {
    Float::set((double)r);
  };
};


void Pot_Input::set_nodeVoltage(double new_nodeVoltage) {
    IO_bi_directional_pu::set_nodeVoltage(new_nodeVoltage);
    pot->compute_VthZth();
}

void Potentiometer::compute_VthZth() {
    double total_r = 20000;
    double ratio = 0.5;
    double Ios, Iss;
    double Vth, Zth;
    double Pin1V, Pin1Z, Pin3V, Pin3Z;

    if(this->rtotalAttr)
      this->rtotalAttr->get(total_r);
    if(this->ratioAttr)
      this->ratioAttr->get(ratio);

    Pin1V = this->pin1->get_nodeVoltage();
    Pin3V = this->pin3->get_nodeVoltage();
    Pin1Z = 0;
    Pin3Z = 0;
    Ios = (Pin1V - Pin3V) /
          (Pin1Z + total_r + Pin3Z);

    Vth = Ios * (Pin3Z + total_r * ratio) + Pin3V;

    Iss = Pin1V / (Pin1Z + (1.0 - ratio) * total_r) +
          Pin3V / (Pin3Z + ratio * total_r);
    Zth = Vth / Iss;
    //printf("ratio = %f, resistance = %f\n", ratio, total_r);
    //printf("Pin1: Zth: %f Vth: %f\n", Pin1Z, Pin1V);
    //printf("Pin3: Zth: %f Vth: %f\n", Pin3Z, Pin3V);
    //printf("Pin2: Zth: %f Vth: %f\n\n", Zth, Vth);
    this->pin2->set_Zth(Zth);
    this->pin2->set_Vth(Vth);
    this->pin2->set_Vpullup(Vth);
    this->pin2->updateNode();
}

//--------------------------------------------------------------
// create_iopin_map
//
//  This is where the information for the Module's package is defined.
// Specifically, the I/O pins of the module are created.

void Potentiometer::create_iopin_map(void)
{

  create_pkg(3);

  assign_pin(1, pin1);
  package->set_pin_position(1,2.7); // Position pin on middle right side of package
  assign_pin(2, pin2);
  package->set_pin_position(2,2.5); // Position pin on middle right side of package
  assign_pin(3, pin3);
  package->set_pin_position(3,2.1); // Position pin on middle right side of package

}

//--------------------------------------------------------------
// GUI
static void
scale_cb (GtkWidget *scale, Potentiometer *pot)

{
  double r;
  if (pot) {
    r = gtk_range_get_value(GTK_RANGE(scale));
    //printf("Setting ratio to: %f\n", r);
    pot->ratioAttr->set_only(r);
  }
}

void
Potentiometer::updateSlider(double r)

{
  if(scale)
    gtk_range_set_value(GTK_RANGE(scale), r);
}

void Potentiometer::create_widget(Potentiometer *pot)
{

  GtkWidget	*box1;

  box1 = gtk_vbox_new (FALSE, 0);

  scale = gtk_vscale_new_with_range(0, 1.0, 0.001);
  gtk_scale_set_digits(GTK_SCALE(scale), 3);
  gtk_widget_set_size_request(scale, -1, 200);
  gtk_range_set_inverted(GTK_RANGE(scale), 1);

  gtk_signal_connect (GTK_OBJECT (scale), "value_changed",
		      GTK_SIGNAL_FUNC (scale_cb), (gpointer)pot);

  gtk_widget_show(scale);
  gtk_box_pack_start (GTK_BOX (box1), scale, FALSE, FALSE, 0);


  // Tell gpsim which widget to use in breadboard.
  pot->set_widget(box1);
}

//--------------------------------------------------------------
// construct
Module * Potentiometer::construct(const char *_new_name=0)
{

  Potentiometer *potP = new Potentiometer(_new_name) ;

  potP->create_widget(potP);

  return potP;

}

Potentiometer::Potentiometer(const char * _name) : Module(_name, "Potentiometer")
{
  scale = NULL;
  pin1 = new Pot_Input(this, (name() + ".pin1").c_str());
  pin2 = new IO_bi_directional_pu((name() + ".pin2").c_str());
  pin3 = new Pot_Input(this, (name() + ".pin3").c_str());

  create_iopin_map();

  rtotalAttr = new RTotalAttribute(this);
  ratioAttr  = new RatioAttribute(this);
  addSymbol(rtotalAttr);
  addSymbol(ratioAttr);

  pin1->set_Cth(0);
  pin2->set_Cth(0);
  pin3->set_Cth(0);
  //pin1->setDriving(false);
  pin1->setDriving(false);
  //pin3->setDriving(false);
  //pin1->update_pullup('0',true);
  pin2->update_pullup('1',true);
  //pin3->update_pullup('0',true);
}

Potentiometer::~Potentiometer(void)
{
  removeSymbol(rtotalAttr);
  removeSymbol(ratioAttr);
  delete pin1;
  delete pin2;
  delete pin3;
}

#endif // HAVE_GUI
